/*++

Copyright (c) 2004, Intel Corporation                                                         
All rights reserved. This program and the accompanying materials                          
are licensed and made available under the terms and conditions of the BSD License         
which accompanies this distribution.  The full text of the license may be found at        
http://opensource.org/licenses/bsd-license.php                                            
                                                                                          
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             

Module Name:

  Mousehid.c

Abstract:
  Parse mouse hid descriptor

--*/

#include "Tiano.h"
#include "EfiDriverLib.h"
#include EFI_PROTOCOL_DEFINITION (SimplePointer)
#include "Hid.h"
#include "MouseHid.h"

//
// Get an item from report descriptor
//
STATIC
UINT8 *
GetNextItem (
  IN  UINT8    *StartPos,
  IN  UINT8    *EndPos,
  OUT HID_ITEM *HidItem
  )
/*++

Routine Description:

  Get Next Item

Arguments:

  StartPos  - Start Position
  EndPos    - End Position
  HidItem   - HidItem to return

Returns:
  Position

--*/
{
  UINT8 Temp;

  if ((EndPos - StartPos) <= 0) {
    return NULL;
  }

  Temp = *StartPos;
  StartPos++;
  //
  // bit 2,3
  //
  HidItem->Type = (UINT8) ((Temp >> 2) & 0x03);
  //
  // bit 4-7
  //
  HidItem->Tag = (UINT8) ((Temp >> 4) & 0x0F);

  if (HidItem->Tag == HID_ITEM_TAG_LONG) {
    //
    // Long Items are not supported by HID rev1.0,
    // although we try to parse it.
    //
    HidItem->Format = HID_ITEM_FORMAT_LONG;

    if ((EndPos - StartPos) >= 2) {
      HidItem->Size = *StartPos++;
      HidItem->Tag  = *StartPos++;

      if ((EndPos - StartPos) >= HidItem->Size) {
        HidItem->Data.LongData = StartPos;
        StartPos += HidItem->Size;
        return StartPos;
      }
    }
  } else {
    HidItem->Format = HID_ITEM_FORMAT_SHORT;
    //
    // bit 0, 1
    //
    HidItem->Size   = (UINT8) (Temp & 0x03);
    switch (HidItem->Size) {

    case 0:
      //
      // No data
      //
      return StartPos;

    case 1:
      //
      // One byte data
      //
      if ((EndPos - StartPos) >= 1) {
        HidItem->Data.U8 = *StartPos++;
        return StartPos;
      }

    case 2:
      //
      // Two byte data
      //
      if ((EndPos - StartPos) >= 2) {
        EfiCopyMem (&HidItem->Data.U16, StartPos, sizeof (UINT16));
        StartPos += 2;
        return StartPos;
      }

    case 3:
      //
      // 4 byte data, adjust size
      //
      HidItem->Size++;
      if ((EndPos - StartPos) >= 4) {
        EfiCopyMem (&HidItem->Data.U32, StartPos, sizeof (UINT32));
        StartPos += 4;
        return StartPos;
      }
    }
  }

  return NULL;
}

STATIC
UINT32
GetItemData (
  IN  HID_ITEM *HidItem
  )
/*++

Routine Description:

  Get Item Data

Arguments:

  HidItem - HID_ITEM

Returns:
  HidItem Data
  

--*/
{
  //
  // Get Data from HID_ITEM structure
  //
  switch (HidItem->Size) {

  case 1:
    return HidItem->Data.U8;

  case 2:
    return HidItem->Data.U16;

  case 4:
    return HidItem->Data.U32;
  }

  return 0;
}

STATIC
VOID
ParseLocalItem (
  IN  USB_MOUSE_DEV   *UsbMouse,
  IN  HID_ITEM        *LocalItem
  )
/*++

Routine Description:

  Parse Local Item

Arguments:

  UsbMouse  - USB_MOUSE_DEV
  LocalItem - Local Item

Returns:

--*/
{
  UINT32  Data;

  if (LocalItem->Size == 0) {
    //
    // No expected data for local item
    //
    return ;
  }

  Data = GetItemData (LocalItem);

  switch (LocalItem->Tag) {

  case HID_LOCAL_ITEM_TAG_DELIMITER:
    //
    // we don't support delimiter here
    //
    return ;

  case HID_LOCAL_ITEM_TAG_USAGE:
    return ;

  case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
    if (UsbMouse->PrivateData.ButtonDetected) {
      UsbMouse->PrivateData.ButtonMinIndex = (UINT8) Data;
    }

    return ;

  case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
    {
      if (UsbMouse->PrivateData.ButtonDetected) {
        UsbMouse->PrivateData.ButtonMaxIndex = (UINT8) Data;
      }

      return ;
    }
  }
}

STATIC
VOID
ParseGlobalItem (
  IN  USB_MOUSE_DEV   *UsbMouse,
  IN  HID_ITEM        *GlobalItem
  )
{
  UINT8 UsagePage;

  switch (GlobalItem->Tag) {
  case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
    {
      UsagePage = (UINT8) GetItemData (GlobalItem);

      //
      // We only care Button Page here
      //
      if (UsagePage == 0x09) {
        //
        // Button Page
        //
        UsbMouse->PrivateData.ButtonDetected = TRUE;
        return ;
      }
      break;
    }

  }
}


STATIC
VOID
ParseMainItem (
  IN  USB_MOUSE_DEV   *UsbMouse,
  IN  HID_ITEM        *MainItem
  )
/*++

Routine Description:

  Parse Main Item

Arguments:

  UsbMouse  - TODO: add argument description
  MainItem  - HID_ITEM to parse

Returns:

  VOID

--*/
{
  //
  // we don't care any main items, just skip
  //
  return ;
}

STATIC
VOID
ParseHidItem (
  IN  USB_MOUSE_DEV   *UsbMouse,
  IN  HID_ITEM        *HidItem
  )
/*++

Routine Description:

  Parse Hid Item

Arguments:

  UsbMouse  - USB_MOUSE_DEV
  HidItem   - HidItem to parse

Returns:

  VOID

--*/
{
  switch (HidItem->Type) {

  case HID_ITEM_TYPE_MAIN:
    //
    // For Main Item, parse main item
    //
    ParseMainItem (UsbMouse, HidItem);
    break;

  case HID_ITEM_TYPE_GLOBAL:
    //
    // For global Item, parse global item
    //
    ParseGlobalItem (UsbMouse, HidItem);
    break;

  case HID_ITEM_TYPE_LOCAL:
    //
    // For Local Item, parse local item
    //
    ParseLocalItem (UsbMouse, HidItem);
    break;
  }
}
//
// A simple parse just read some field we are interested in
//
EFI_STATUS
ParseMouseReportDescriptor (
  IN  USB_MOUSE_DEV   *UsbMouse,
  IN  UINT8           *ReportDescriptor,
  IN  UINTN           ReportSize
  )
/*++

Routine Description:

  Parse Mouse Report Descriptor

Arguments:

  UsbMouse          - USB_MOUSE_DEV
  ReportDescriptor  - Report descriptor to parse
  ReportSize        - Report descriptor size

Returns:

  EFI_DEVICE_ERROR - Report descriptor error
  EFI_SUCCESS      - Success

--*/
{
  UINT8     *DescriptorEnd;
  UINT8     *ptr;
  HID_ITEM  HidItem;

  DescriptorEnd = ReportDescriptor + ReportSize;

  ptr           = GetNextItem (ReportDescriptor, DescriptorEnd, &HidItem);

  while (ptr != NULL) {
    if (HidItem.Format != HID_ITEM_FORMAT_SHORT) {
      //
      // Long Format Item is not supported at current HID revision
      //
      return EFI_DEVICE_ERROR;
    }

    ParseHidItem (UsbMouse, &HidItem);

    ptr = GetNextItem (ptr, DescriptorEnd, &HidItem);
  }

  UsbMouse->NumberOfButtons                 = (UINT8) (UsbMouse->PrivateData.ButtonMaxIndex - UsbMouse->PrivateData.ButtonMinIndex + 1);
  UsbMouse->XLogicMax                       = UsbMouse->YLogicMax = 127;
  UsbMouse->XLogicMin                       = UsbMouse->YLogicMin = -127;

  return EFI_SUCCESS;
}
